package com.catchman.aspect;

import com.alibaba.fastjson.JSONObject;
import com.catchman.annotation.OperationLog;
import com.catchman.entity.SysLog;
import com.catchman.entity.SysUser;
import com.catchman.service.SysLogService;
import com.catchman.utils.IpAddressUtil;
import com.catchman.utils.ThrowableUtil;
import com.catchman.vo.ResponseModel;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 系统日志：切面处理类，在需要配置日志的方法或者类上注明@OperationLog 信息
 *
 * @author catchaman
 */
@Aspect
@Component
public class SysLogAspect {
    private static final int LOG_RESULT_LENGTH = 255;
    @Autowired
    private SysLogService sysLogService;
    private long currentTime = 0L;

    /**
     * 定义切点 @Pointcut
     * 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.catchman.annotation.OperationLog)")
    public void logPoinCut() { }

    /**
     * 切面 配置通知
     *
     * @param joinPoint
     */
    @Around(value = "logPoinCut()")
    public void saveSysLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        currentTime = System.currentTimeMillis();
        // 抛出异常
        result = joinPoint.proceed();
        //保存日志
        SysLog sysLog = new SysLog();
        // 日志类型 1一般日志 2错误日志
        sysLog.setLogType(1);
        // 请求时间
        sysLog.setTime(System.currentTimeMillis() - currentTime);
        saveSysLog(joinPoint, sysLog, result);
    }

    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param throwable exception
     */
    @AfterThrowing(pointcut = "logPoinCut()", throwing = "throwable")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable throwable) {
        SysLog sysLog = new SysLog();
        // 日志类型 1一般日志 2错误日志
        sysLog.setLogType(2);
        // 请求时间
        sysLog.setTime(System.currentTimeMillis() - currentTime);
        saveSysLog(joinPoint, sysLog, ThrowableUtil.getStackTrace(throwable));
    }

    /**
     *  生成日志记录，并保存到数据库
     * @param joinPoint 切点
     * @param sysLog
     * @param result 需要记录到 result 的信息
     */
    private void saveSysLog(JoinPoint joinPoint, SysLog sysLog, Object result) {
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();

        //获取操作
        OperationLog operation = method.getAnnotation(OperationLog.class);
        if (operation != null) {
            String value = operation.value();
            //保存获取的操作
            sysLog.setOperation(value);
        }

        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();

        //获取请求的方法名
        String methodName = method.getName();
        sysLog.setMethod(className + "." + methodName);

        //请求的参数
        Object[] args = joinPoint.getArgs();
        //将参数所在的数组转换成json
        String params = null;
        try {
            params = JSONObject.toJSONString(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        sysLog.setParams(params);

        //请求的时间
        sysLog.setCreated(LocalDateTime.now());

        //获取用户id (根据当前安全框架来获取登录用户信息)
        /**
         * shiro 方案，从session取出用户信息
         */
        Object principal = SecurityUtils.getSubject().getPrincipal();
        if (null != principal) {
            SysUser user = (SysUser) principal;
            sysLog.setUserId(user.getUid());
        }

        //获取用户ip地址
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        sysLog.setIp(IpAddressUtil.getIpAdrress(request));

        //操作返回信息（只保留前255字符）
        String str;
        if (result instanceof ResponseModel) {
            str = ((ResponseModel) result).get("msg").toString();
        } else {
            str = result.toString();
        }

        if (str.length() > LOG_RESULT_LENGTH) {
            sysLog.setResult(str.substring(0, LOG_RESULT_LENGTH - 1));
        } else {
            sysLog.setResult(str);
        }
        sysLogService.save(sysLog);
    }

}
